// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:io';

import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';

import '../../tool/spec/api.dart';
import '../../tool/spec/from_html.dart';

/// Define tests to fail if there's no mention in the coverage file.
void main() {
  Api api;
  File coverageFile;
  String pathPrefix;

  // parse the API file
  if (FileSystemEntity.isFileSync(
    path.join('tool', 'spec', 'spec_input.html'),
  )) {
    api = readApi('.');
    pathPrefix = '.';
  } else {
    api = readApi(path.join('pkg', 'analysis_server'));
    pathPrefix = path.join('pkg', 'analysis_server');
  }

  coverageFile = File(
    path.join(pathPrefix, 'test', 'integration', 'coverage.md'),
  );
  var lines = coverageFile.readAsLinesSync();

  // ## server domain
  var coveredDomains =
      lines
          .where((line) => line.startsWith('## ') && line.endsWith(' domain'))
          .map(
            (line) =>
                line
                    .substring('##'.length, line.length - 'domain'.length)
                    .trim(),
          )
          .toSet();

  // Remove any ' (test failed)' suffixes.
  lines =
      lines.map((String line) {
        var index = line.indexOf('(');
        return index != -1 ? line.substring(0, index).trim() : line;
      }).toList();

  // - [ ] server.getVersion
  var allMembers =
      lines
          .where((line) => line.startsWith('- '))
          .map((line) => line.substring('- [ ]'.length).trim())
          .toSet();
  var coveredMembers =
      lines
          .where((line) => line.startsWith('- [x]'))
          .map((line) => line.substring('- [x]'.length).trim())
          .toSet();

  // generate domain tests
  for (var domain in api.domains) {
    group('integration coverage of ${domain.name}', () {
      // domain
      test('domain', () {
        if (!coveredDomains.contains(domain.name)) {
          fail('${domain.name} domain not found in ${coverageFile.path}');
        }
      });

      // requests
      group('request', () {
        for (var request in domain.requests) {
          var fullName = '${domain.name}.${request.method}';
          test(fullName, () {
            if (!allMembers.contains(fullName)) {
              fail('$fullName not found in ${coverageFile.path}');
            }

            var fileName = getCamelWords(
              request.method,
            ).map((s) => s.toLowerCase()).join('_');
            var testName = path.join(domain.name, '${fileName}_test.dart');
            var testPath = path.join(
              pathPrefix,
              'test',
              'integration',
              testName,
            );

            // Test that if checked, a test file exists; if not checked, no such
            // file exists.
            var fileExists = FileSystemEntity.isFileSync(testPath);
            var isMarkedAsCovered = coveredMembers.contains(fullName);
            expect(
              fileExists,
              isMarkedAsCovered,
              reason:
                  isMarkedAsCovered
                      ? '$testName marked as covered but has no test at $testPath'
                      : '$testName marked as not covered has test at $testPath',
            );
          });
        }
      });
    });
  }

  // validate no unexpected domains
  group('integration coverage', () {
    test('no unexpected domains', () {
      for (var domain in coveredDomains) {
        expect(api.domains.map((d) => d.name), contains(domain));
      }
    });
  });
}
